package com.sebscape.sebcms.pages.domain;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.jdo.annotations.IdGeneratorStrategy;
import javax.jdo.annotations.IdentityType;
import javax.jdo.annotations.NotPersistent;
import javax.jdo.annotations.PersistenceCapable;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.PrimaryKey;
import javax.jdo.annotations.Unique;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
import com.google.appengine.api.datastore.Text;
import com.sebscape.sebcms.persistence.domain.IPersistedObject;
import com.sebscape.sebcms.persistence.domain.IUpdateable;
import com.sebscape.sebcms.security.domain.Group;
import com.sebscape.sebcms.security.domain.User;

@PersistenceCapable(identityType = IdentityType.APPLICATION, detachable = "true")
@Unique(name="PAGE_UNIQUE_IDX", members={"customUrl"})
public class Page implements IPersistedObject, IUpdateable<Page>{
	
	private static Log log = LogFactory.getLog( Page.class );
	
	@PrimaryKey
	@Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
	private Long id;

	@Persistent
	private String name;
	
	@Persistent
	private String description;
	
	@Persistent
	private String customUrl;
	
	@Persistent(defaultFetchGroup="true")
	private Text content;
	
	@Persistent
	private Integer version;
	
	@Persistent
	private String status;
	
	@Persistent
	private Boolean isLive;
	
	@Persistent
	private Boolean isPublic;
	
	@Persistent
	private Set<Key> groupKeySet;
	
	@NotPersistent
	private List<Group> groups;

	/**
	 * @param name
	 * @param description
	 * @param customUrl
	 * @param content
	 */
	public Page(String name, String description, String customUrl, String content) {
		this();
		this.name = name;
		this.description = description;
		this.customUrl = customUrl;
		this.content = new Text( content );
	}

	/**
	 * Minimal Constructor.
	 */
	public Page() {
		super();
		this.groupKeySet = new HashSet<Key>();
		this.groups = new ArrayList<Group>();
		this.isPublic = Boolean.FALSE;
		this.isLive = Boolean.TRUE;
	}


	public Boolean isViewableByGroup( Group group ){
		log.debug("checking group = " + group );
		// Handle the nulls.
		if( this.isPublic == null || isPublic )
			return Boolean.TRUE;
		if( group == null ){
			return Boolean.FALSE;
		}
		// now loop through groups assigned to this page and return TRUE if match found.
		for( Group g: this.groups ){
			log.debug( "comparing userGroup " + group + " to pageGroup " + g);
			if( g.equals( group ) ){
				return Boolean.TRUE;
			}
		}
		// if we reach this point, no match was found.
		return Boolean.FALSE;
	}

	/**
	 * IFF the page is PRIVATE, loop through the User's Groups and see if
	 * that group grants them access to the page. If the page is NOT PRIVATE
	 * this method will always return TRUE.
	 * @param user
	 * @return TRUE if page is PUBLIC or if the User has a Group matching at least 
	 * 				one this Page's Group; otherwise, FALSE. 
	 */
	public Boolean isViewableByUser( User user ){
		if( this.isPublic == null || isPublic ){
			log.debug("Page.isViewableByUser: Page is PUBLIC so return TRUE.");
			return Boolean.TRUE;
		} else if( user == null || user.getGroups() == null ){
			log.debug("Page.isViewableByUser: User is NULL so return FALSE.");
			return Boolean.FALSE;
		}
		// now loop through groups assigned to this page and return TRUE if match found.
		for( Group g: user.getGroups() ){
			if( this.isViewableByGroup( g ) ){
				log.debug("Page.isViewableByUser: Both page and User include Group \""+ g.getName() +"\" so return TRUE.");
				return Boolean.TRUE;
			}
		}
		// if we reach this point, no match was found.
		log.debug("Page.isViewableByUser: no matching Groups found so return FALSE.");
		return Boolean.FALSE;
	}
	
	////////////////////////////
	// getters and Setters
	////////////////////////////
	/**
	 * @return the id
	 */
	public Long getId() {
		return id;
	}

	/**
	 * @param id the id to set
	 */
	public void setId(Long id) {
		this.id = id;
	}

	/**
	 * @return the name
	 */
	public String getName() {
		return name;
	}

	/**
	 * @param name the name to set
	 */
	public void setName(String name) {
		this.name = name;
	}

	/**
	 * @return the description
	 */
	public String getDescription() {
		return description;
	}

	/**
	 * @param description the description to set
	 */
	public void setDescription(String description) {
		this.description = description;
	}

	/**
	 * @return the customUrl
	 */
	public String getCustomUrl() {
		return customUrl;
	}

	/**
	 * @param customUrl the customUrl to set
	 */
	public void setCustomUrl(String customUrl) {
		this.customUrl = customUrl;
	}

	/**
	 * @return the content
	 */
	public Text getContent() {
		return content;
	}

	/**
	 * @param content the content to set
	 */
	public void setContent(Text content) {
		this.content = content;
	}

	/**
	 * @param content as a String
	 */
	public void setHtml(String content) {
		log.debug("setHtml( String ) begin..." );
		this.content = new Text( content );
	}

	/**
	 * @param content as a String
	 */
	public String getHtml() {
		log.debug("getHtml() begin..." );
		return ( this.content == null? null : this.content.getValue() );
	}
	
	/**
	 * @return the version
	 */
	public Integer getVersion() {
		return version;
	}

	/**
	 * @param version the version to set
	 */
	public void setVersion(Integer version) {
		this.version = version;
	}

	/**
	 * @return the status
	 */
	public String getStatus() {
		return status;
	}

	/**
	 * @param status the status to set
	 */
	public void setStatus(String status) {
		this.status = status;
	}

	/**
	 * @return the isLive
	 */
	public Boolean getIsLive() {
		return isLive;
	}

	/**
	 * @param isLive the isLive to set
	 */
	public void setIsLive(Boolean isLive) {
		this.isLive = isLive;
	}

	/**
	 * @return the negaive of isPrivate
	 */
	public Boolean getIsPublic() {
		return this.isPublic;
	}
	
	/**
	 * @param isPrivate the isPrivate to set
	 */
	public void setIsPublic(Boolean isPublic) {
		log.debug("Page.setIsPublic: value = " + isPublic);
		this.isPublic = isPublic ;
	}
	
	/**
	 * @return the groupKeyList
	 */
	public Set<Key> getGroupKeySet() {
		return groupKeySet;
	}

	/**
	 * @param groupKeyList the groupKeyList to set
	 */
	public void setGroupKeySet(Set<Key> groupKeySet) {
		this.groupKeySet = groupKeySet;
	}

	/**
	 * @return the groups
	 */
	public List<Group> getGroups() {
		return groups;
	}

	/**
	 * @param groups the groups to set
	 */
	public void setGroups(List<Group> groups) {
		this.groups = groups;
		log.debug("Page.setGroups: being... groups = " + groups) ;
		// update the keyset
		if( groups != null ){
		    log.debug("Page.setGroups: setting keys from groups.size()=" + groups.size());
			Set<Key> keySet = new HashSet<Key>();
			for( Group g: groups ){
				Key key = KeyFactory.createKey("Group", g.getId() );
				log.debug("Page.setGroups: added Key" + key.toString() );
				keySet.add( key );
			}
			this.setGroupKeySet( keySet );
		}
	}

	public void setGroupIdList( List<Long> groupIdList ){
		log.debug("setGroupIdList called..." );
		if( groupIdList != null ){
			Set<Key> kSet = new HashSet<Key>();
			for( Long id: groupIdList ){
				Key key = KeyFactory.createKey( "Group", id);
				log.debug("adding key: " + id + " as " + key );
				kSet.add( key );
			}
			this.setGroupKeySet( kSet );
		}
	}

	@Override
	public void update(Page newValues) {
		this.setName( newValues.getName() );
		this.setDescription( newValues.getDescription() );
		this.setCustomUrl( newValues.getCustomUrl() );
		this.setContent( newValues.getContent() );
		this.setVersion( newValues.getVersion() );
		this.setStatus( newValues.getStatus() );
		this.setIsLive( newValues.getIsLive() );
		this.setIsPublic( newValues.getIsPublic() );
		// groups and roles.
		this.setGroupKeySet( newValues.getGroupKeySet() );
	}
		
}
